/*
 * @autor Carlos Padoa
 */
package br.com.felix.fwt.servlet;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;

import br.com.felix.fwt.security.exception.UnauthorizedAccessException;
import br.com.felix.fwt.servlet.exception.FormNotFoundException;
import br.com.felix.fwt.servlet.exception.UpdateFormFieldsException;
import br.com.felix.fwt.ui.Form;
import br.com.felix.fwt.ui.exception.ComponentRenderException;
import br.com.felix.fwt.ui.exception.UserInterfaceInitializationException;

/**
 * The Class FelixWebToolkitServlet is the Front Controller of the
 * framework. All of the website's requests are mapped to this Servlet. It works
 * pretty much like the Struts and JSF controllers. The workflow goes like this:
 * request => load form for the first time => store the form in memory =>
 * present to the client. request => load form from memory => update field
 * values => process form => present to the client.
 */
public abstract class FelixStatefulFrontController implements FrontController{
	

	private Logger logger = null;
	
	/**
	 * The Constant serialVersionUID required by the serializable interface.
	 */
	private static final long serialVersionUID = -576331252796570862L;
	
		
		


	/**
	 * @throws IOException 
	 * @throws ServletException 
	 * @throws FormNotFoundException 
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void processRequestedForm(String requestedForm, FelixWebToolkitServlet felixWebToolkitServlet, HttpServletRequest request, HttpServletResponse resp) throws IOException, ServletException, FormNotFoundException{
		FwtContext threadLocalContext = felixWebToolkitServlet.getFwtContext();
		HttpSession session = request.getSession();
		Form form = (Form) session.getAttribute(requestedForm);
		threadLocalContext.form = form;

		// multipart/form-data
		logger.debug("Content-type: " + request.getContentType());

		if (form == null) {
			try {
				logger.info("Loading form '" + requestedForm + "' for the first time.");
				Class c = Class.forName(requestedForm);
				form = (Form) c.newInstance();
				threadLocalContext.form = form;
				try {
					form.initializeForm();
					session.setAttribute(requestedForm, form);
				}
				catch (UserInterfaceInitializationException e) {
					logger.error("Error initializing form '"+requestedForm+"'. Executing treatment now.",e);
					form = form.catchUserInterfaceInitializationException(e);
					String formName = form.getName();
					logger.info("Executing form now is '" + formName + "'");
					session.setAttribute(formName, form);
				}
				catch (UnauthorizedAccessException e) {
					logger.info("Unauthorized access to form '"+requestedForm+"'. Executing treatment now.",e);
					form = form.catchUnauthorizedAccessException(e);
					String formName = form.getName();
					logger.info("Executing form now is '" + formName + "'");
					session.setAttribute(formName, form);
				}
				form.registerComponents();
			}
			catch (ClassNotFoundException e) {
				String msg = "The following page was not found: '" + requestedForm + "'.";
				logger.fatal(msg, e);
				throw new FormNotFoundException(msg);
			}
			catch (InstantiationException e) {
				String msg = "The following page could not be loaded: '" + requestedForm + "'.";
				logger.fatal(msg, e);
				throw new FormNotFoundException(msg);
			}
			catch (IllegalAccessException e) {
				String msg = "The following page could not be loaded: '" + requestedForm + "'.";
				logger.fatal(msg, e);
				throw new FormNotFoundException(msg);
			}
		}
		else {
			logger.info("Form '" + requestedForm + "' loaded from Session.");
			String action = null;
			try {
				action = form.updateFormFields(request);
				logger.info("Requested action = '" + action + "'");
				if (action != null) {
					if("SVG".equals(action)){
						PrintWriter outWriter = resp.getWriter();
						String svgComponent = request.getParameter("svg");
						logger.info("Printing SVG to output...");
						form.writeSVG(svgComponent, outWriter);
						outWriter.flush();
						return;
					}
					else if("CHART".equals(action)){
					    resp.setContentType("image/png");
					    OutputStream os = resp.getOutputStream();
					    String chartComponent = request.getParameter("chart");
					    logger.info("Printing CHART to output...");
					    form.writeChart(chartComponent, os);
					    os.flush();
					    return;
					}
					else{
						form = form.executeAction(action);
						String formName = form.getName();
						session.setAttribute(formName, form);
						logger.info("Executing form now is '" + formName + "'");
					}
				}
				else{
				    try{
    				    form.reload();
				    }
				    catch (UserInterfaceInitializationException e) {
	                    logger.error("Error reloading form '"+requestedForm+"'. Executing treatment now.",e);
	                    form = form.catchUserInterfaceInitializationException(e);
	                    String formName = form.getName();
	                    logger.info("Executing form now is '" + formName + "'");
	                    session.setAttribute(formName, form);
	                }
				    form.registerComponents();
                    session.setAttribute(requestedForm, form);
                    logger.info("Reloaded form '" + requestedForm + "'");
				}
			}
			catch (UpdateFormFieldsException e) {
				logger.fatal("Error executing form '"+requestedForm+"'. Executing treatment now.",e);
				form = form.catchUpdateFormFieldsException(e);
				String formName = form.getName();
				logger.info("Executing form now is '" + formName + "'");
				session.setAttribute(formName, form);
			}
		}
		PrintWriter out = resp.getWriter();
		// resp.setCharacterEncoding("ISO-8859-1");
		resp.setContentType("text/html; charset=UTF-8");
		if (form != null) {
			try {
			form.write(out);
            }
            catch (ComponentRenderException e) {
                String msg = "Could NOT display form '" + requestedForm + "'. An error ocurred trying to render a component.";
                logger.error(msg, e);
                throw new ServletException(msg, e);
            }
			logger.info("Form '" + requestedForm + "' executed correctly");
		}
		else {
			logger.fatal("Form '" + requestedForm + "' executed INCORRECTLY");
		}
		out.flush();
	}

	

	/**
	 * Returns the String used in the hidden field 'action'.
	 * */
	public static String formAction(Class<?> clazz) {
		return clazz.getSimpleName();
	}
	
	
	/**
	 * Returns a form previously loaded. Null otherwise.
	 * */
	public static <T extends Form> T getLoadedForm(Class<T> clazz){
		FwtContext ctx = FelixWebToolkitServlet.getFwtContext();
		HttpServletRequest req = ctx.request;
		HttpSession session = req.getSession();
		return (T)session.getAttribute(clazz.getCanonicalName());
	}

	
	
	
	/**
	 * Signals that the form's contents are not valid anymore.
	 * And that the form should be reloaded on the next request.
	 * */
	public static void invalidateForm(Form form){
		HttpServletRequest req = FelixWebToolkitServlet.getFwtContext().request;
		HttpSession session = req.getSession();
		session.removeAttribute(form.getClass().getCanonicalName());
	}
}
